# -*- coding: utf-8 -*-
import numpy as np
import math
import json
import sys

# 矩阵相乘 a3=a1*a2
# 对应元素相乘a3=multiply(a1,a2)
# 矩阵点乘 a1*2
# 求逆 a1.I
# 转置 a1.T
# 列和 a1.sum(axis=0)
# 行和 a1.sum(axis=1)


# 获取系统参数
def get_system_param(database, businessCode):
    databaseName = "derivative_data"
    sqlCommand = "select t.param_code, t.param_value \
    from jrzt_bl_param t where t.business_code='" + businessCode + "' and t.valid=1"
    blParams = database.ExecuteSQL(databaseName, sqlCommand)

    sys_param = {}
    for param in blParams:
        sys_param[param[0]] = json.loads(param[1])

    return sys_param


# 根据用户风险偏好和各资产投资限制，得到可投的资产“个数”
def getInvestableAssets(lambda_i, assetsAmount, categoryLimits):
    #
    INVESTABLE_ASSETS_MATRIX = [
        [1, 1, 1, 1, 1],
        [1, 2, 2, 3, 3],
        [2, 2, 3, 2, 2],
        [2, 3, 3, 4, 4],
        [2, 3, 4, 4, 5],
        [2, 3, 5, 5, 5]
    ]

    i = 0
    j = 0
    # 修改投资档位
    if lambda_i >= 6 and lambda_i <= 7:
        j = 0
    elif lambda_i >= 5 and lambda_i < 6:
        j = 1
    elif lambda_i >= 4 and lambda_i < 5:
        j = 2
    elif lambda_i >= 3 and lambda_i < 4:
        j = 3
    elif lambda_i >= 2 and lambda_i < 3:
        j = 4

    if assetsAmount <= max(categoryLimits[0]):
        i = 0
    elif assetsAmount < max(categoryLimits[1]):
        i = 1
    elif assetsAmount < max(categoryLimits[2]):
        i = 2
    elif assetsAmount < max(categoryLimits[3]):
        i = 3
    elif assetsAmount < max(categoryLimits[4]):
        i = 4
    elif assetsAmount >= max(categoryLimits[4]):
        i = 5

    investable_assets = []

    last_assets = INVESTABLE_ASSETS_MATRIX[i][j]

    add_val = 1
    for ind in range(1, len(INVESTABLE_ASSETS_MATRIX[0])+1):
        if last_assets == ind:

            investable_assets.append(add_val)
            add_val = 0
        else:
            investable_assets.append(add_val)

    if i == 1 and j >= 3:
        investable_assets = [1, 0, 1, 0, 0]
    if i == 2 and j >= 3:
        investable_assets = [1, 1, 1, 0, 0]

    return investable_assets, j


# 资产权重上下限调整
# investable_assets:可投资
# lowWeight：资产下限
# highWeight：资产上限
def checkAndAdjustWeight(asset_investable, lowWeight, highWeight, riskPrefer):
    low_sum = 0
    high_sum = 0
    for i in range(len(lowWeight)):
        lowWeight[i] = lowWeight[i] * asset_investable[i]
        highWeight[i] = highWeight[i] * asset_investable[i]
        low_sum += lowWeight[i]
        high_sum += highWeight[i]

    # 检查下限不能大于1
    if low_sum > 1:
        # 需要调整
        if riskPrefer >= 4:
            order = "0,1,2,3,4"
        elif riskPrefer < 4:
            order = "0,2,1,3,4"

        arr = order.split(',')
        sum = 0
        for m in range(0, len(arr)):
            if sum + lowWeight[int(arr[m])] <= 1:
                pass
            else:
                # 下限之和大于1的资产，上下界约束都设为0--不投该资产，继续尝试下一项
                lowWeight[int(arr[m])] = 0
                highWeight[int(arr[m])] = 0
        # 若只剩下固收，则将其上限提高到1
        if len([x for x in lowWeight if x != 0]) <= 1:
            highWeight[0] = 1

    ##检查上限不能小于1
    if high_sum < 1:
        ##需要调整
        print("上限和小于1！！！需要调整----%s" % highWeight)
        for i in range(0, len(highWeight)):
            highWeight[i] = highWeight[i] * 1 / high_sum
        print("调整后，%s" % highWeight)

    return lowWeight, highWeight


def getUserConstraint(mConsume,
                      btRemain,
                      totalAssetAmt,
                      riskPrefer,
                      asset_limit_coefficients,
                      least_invest_amount, upper_limits):

    # Max{Max[Min（月支出*2+流动性变动项 – 月收入 + 白条/金条当月应还总额，用户持仓总金额 + 用户投资金额），定期持仓总金额]+a，b}
    num = max(min(mConsume * 2 + btRemain, totalAssetAmt), 0)  # 流动支出
    a = np.mat([1, 1, 1, 1, 1]).T * np.mat([num, 0])
    mat_asset_limit_coefficients = np.mat(asset_limit_coefficients) + a  ##下限加上流动支出，上限不变
    #     print(mat_asset_limit_coefficients)

    asset_investable, j_index = getInvestableAssets(riskPrefer, totalAssetAmt, mat_asset_limit_coefficients.tolist())

    ###该资产属于固收或者债券
    temp1 = [[0, 1, 2], [0, 1, 2], [0, -1, 7], [0, -1, 7], [0, -1, 7]]
    mat_minBoundCoefficients = np.mat(least_invest_amount).T * np.mat([1, 0, 0]) + np.mat(temp1)
    #     print(mat_minBoundCoefficients)

    ##maxBoundCoefficients
    # 规则：根据保守型用户和激进型用户在资产i上的上限计算资产比例上限时各资产需要的参数
    mat_upper_limits = np.mat(upper_limits)
    coefficient_1 = (mat_upper_limits * np.mat([1, -1]).T + np.mat([0, 0, 0.1, 0, 0]).T) / math.log(3.5)
    coefficient_2 = mat_upper_limits[:, 1] - coefficient_1 * math.log(2)
    mat_maxBoundCoefficients = np.hstack((coefficient_1, coefficient_2))
    #     print("mat_maxBoundCoefficients:",mat_maxBoundCoefficients)

    m0 = mat_minBoundCoefficients[:, 0]
    #     m1=mat_minBoundCoefficients[:,1]
    #     m2=mat_minBoundCoefficients[:,2]

    # print(mat_minBoundCoefficients)
    # print(m0,m1,m2)
    m12 = np.mat([(1 + x[1] * math.log(riskPrefer / x[2])) for x in temp1]).T
    # print(m12)

    a2 = np.mat([num / totalAssetAmt, -1 * sys.float_info.max, -1 * sys.float_info.max, -1 * sys.float_info.max,
                 -1 * sys.float_info.max]).T
    a3 = np.multiply(m0, m12) / totalAssetAmt
    # print(a2,a3)
    mat_lowWeight = np.hstack((a2, a3)).max(axis=1)  ##[max(a2,a3),  a3,a3,a3,a3 ]
    #     print("mat_lowWeight:",mat_lowWeight)

    mat_maxBound = mat_maxBoundCoefficients * np.mat([math.log(riskPrefer), 1]).T
    #     print("mat_maxBound:",mat_maxBound)
    mat_highWeight = np.hstack((mat_lowWeight, mat_maxBound)).max(axis=1)

    #     print("mat_highWeight:",mat_highWeight)

    lowWeight = mat_lowWeight.T.tolist()[0]
    highWeight = mat_highWeight.T.tolist()[0]

    bnds_min, bnds_max = checkAndAdjustWeight(asset_investable, lowWeight, highWeight, riskPrefer)
    #     print(bnds_min, bnds_max)

    mat_weight = np.mat([bnds_min, bnds_max]).T

    return mat_weight.tolist()



def test_service(database):
    business_code = 'JDZT'
    consume_monthly_amount = 3000
    bt_balance = 0
    total_asset_amount = 20000
    risk_prefer = 2.5
    #
    asset_limit_coefficients = [[-5000, 2000], [-4000, 3000], [-2000, 5000], [3000, 10000], [5000, 12000]]
    upper_limits = [[1, 0.2], [0.6, 0.4], [0, 0.6], [0, 0.2], [0, 0.4]]
    least_invest_amount = [100, 200, 200, 200, 200]
    #
    weight_constraint = getUserConstraint(consume_monthly_amount, bt_balance, total_asset_amount, risk_prefer,
                                          asset_limit_coefficients, least_invest_amount, upper_limits)

    print(weight_constraint)


if __name__ == '__main__':
    #
    from Core.Config import *
    pathfilename = os.getcwd() + "\..\Config\config2.json"
    config = Config(pathfilename)
    database = config.DataBase("JDMySQL")
    #
    test_service(database)

    p = get_system_param(database, "JDZT")
    print(p)